package com.zmn.plat.business.impl.price.meter3;

import com.google.common.collect.Lists;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dto.KvDTO;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.plat.business.interfaces.channel.ChannelServiceCategoryBService;
import com.zmn.plat.business.interfaces.pilot.ChannelPriceBService;
import com.zmn.plat.business.interfaces.price.meter3.Meter3ProductPriceBService;
import com.zmn.plat.business.interfaces.price.meter3.MeterFlow3CommonBService;
import com.zmn.plat.business.interfaces.price.meter3.MeterFlow3ItemBService;
import com.zmn.plat.business.interfaces.product.ServProductBService;
import com.zmn.plat.common.constant.ItemCategoryConsts;
import com.zmn.plat.common.constant.OnePriceConsts;
import com.zmn.plat.common.constant.ProductConsts;
import com.zmn.plat.common.dto.price.ProductPriceQuery;
import com.zmn.plat.common.dto.price.meter3.ItemDRO3;
import com.zmn.plat.common.dto.price.meter3.ItemPriceQuery3;
import com.zmn.plat.common.dto.price.meter3.ProductPriceDRO3;
import com.zmn.plat.common.dto.price.meter3.ProductTariffTypeDRO;
import com.zmn.plat.common.exception.PlatException;
import com.zmn.plat.manager.dictionary.ProductDict;
import com.zmn.plat.model.dto.price.PriceCalDTO;
import com.zmn.plat.model.dto.product.ProductRelationDTO;
import com.zmn.plat.model.entity.pilot.SupportPilotVO;
import com.zmn.plat.model.entity.product.ServProduct;
import com.zmn.plat.model.entity.product.ServProductQuery;
import com.zmn.plat.model.entity.product.tariff.item.ProductItemQuery;
import com.zmn.plat.model.entity.service.group.ServiceGroup;
import com.zmn.plat.model.entity.service.tariff.ServiceTariff;
import com.zmn.plat.services.interfaces.product.ServProductService;
import com.zmn.plat.services.interfaces.product.tariff.tariffproductitem3.TariffProductItem3Service;
import com.zmn.plat.services.interfaces.service.group.ServiceGroupService;
import com.zmn.plat.services.interfaces.service.tariff.ServiceTariffService;
import com.zmn.plat.utils.bean.BeanCopyUtils;
import com.zmn.plat.utils.bean.PageUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 描述: 计价器3.0产品默认价格实现类
 *
 * @author guoqiao
 * @since 2021/11/22 17:15
 */
@Service
@Slf4j
public class Meter3ProductPriceBServiceImpl implements Meter3ProductPriceBService {

    @Resource
    ServProductBService servProductBService;
    @Resource
    ServProductService servProductService;
    @Resource
    ChannelPriceBService channelPriceBService;
    @Resource
    ChannelServiceCategoryBService channelServiceCategoryBService;
    @Resource
    ServiceTariffService serviceTariffService;
    @Resource
    ServiceGroupService serviceGroupService;
    @Resource
    TariffProductItem3Service tariffProductItem3Service;
    @Resource
    MeterFlow3CommonBService meterFlow3CommonBService;
    @Resource
    MeterFlow3ItemBService meterFlow3ItemBService;

    // 分批次处理的数量
    private final static int DEFAULT_PAGE_SIZE = 1;
    // 开启的线程池核心线程数
    private final static int CORE_POOL_SIZE = 5;
    // 开启的线程池最大线程数
    private final static int MAX_POOL_SIZE = 6;
    // 开启的线程池闲置时间
    private final static int KEEP_ALIVE_TIME = 10;

    @Override
    @Cacheable(cacheNames = "redis10m", key = "'price3:listProductPriceByQuery:ProductPriceQuery:'+#p0", unless = "#result == null")
    public List<ProductPriceDRO3> listProductPriceByQuery(ProductPriceQuery query) {

        List<Integer> productIdList = query.getProductIdList();
        // 处理完毕之后的数据
        List<ProductPriceDRO3> droList = new ArrayList<>();

        if (productIdList.size() <= DEFAULT_PAGE_SIZE) {
            droList = doExcuteHander(query);
        } else {
            // 待处理的数据分批次处理
            List<ProductPriceQuery> queryList = new ArrayList<>();
            // 内存分页，构造分批次的数据
            int totalCount = productIdList.size();
            int page = totalCount % DEFAULT_PAGE_SIZE == 0 ? totalCount / DEFAULT_PAGE_SIZE : totalCount / DEFAULT_PAGE_SIZE + 1;
            for (int i = 1; i <= page; i++) {
                ProductPriceQuery sub = new ProductPriceQuery();
                BeanCopyUtils.copyProperties(query, sub);
                sub.setProductIdList(PageUtils.listPage(productIdList, i, DEFAULT_PAGE_SIZE));
                queryList.add(sub);
            }

            // 开启多线程，以当前处理的页数为队列长度
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new ArrayBlockingQueue<>(productIdList.size()));
            CompletableFuture<List<ProductPriceDRO3>>[] completableFutures = null;
            try {
                completableFutures = queryList.stream().map(excuteQuery -> CompletableFuture.supplyAsync(() -> doExcuteHander(excuteQuery), threadPoolExecutor)).toArray(CompletableFuture[]::new);
                // 等待所有任务执行完
                CompletableFuture.allOf(completableFutures).join();
                // 归并返回值
                for (CompletableFuture<List<ProductPriceDRO3>> completableFuture : completableFutures) {
                    droList.addAll(completableFuture.get());
                }
            } catch (Exception e) {
                log.error("多线程跑价格计算失败：MSG:{}", e.getMessage(), e);
            } finally {
                // 关闭线程池
                threadPoolExecutor.shutdown();
            }
        }
        return droList;
    }

    private List<ProductPriceDRO3> doExcuteHander(ProductPriceQuery query) {
        List<Integer> productIdList = query.getProductIdList();
        Integer channelId = query.getChannelId(); // 原始渠道id,试点使用该字段
        Integer cityId = query.getCityId();
        Integer showType = query.getShowType();
        Integer bizType = query.getBizType();

        // 判断渠道是否开通分类，如果已开通则取当前渠道，反之取上级渠道信息
        Integer avaliableChannelId = channelId;
        try {
            avaliableChannelId = channelServiceCategoryBService.getAvailableCategoryChannelId(channelId);
            query.setChannelId(avaliableChannelId);
            log.info("原渠道：{}，可用渠道：{}", channelId, avaliableChannelId);
        } catch (PlatException e) {
            log.error("无可用渠道，全部30元起");
            return this.createNotSupportOnePriceDRO(new HashSet<>(productIdList), null, query);
        }

        // 出参集合
        List<ProductPriceDRO3> droList = new ArrayList<>(productIdList.size());

        // 查询前台、后台、计价器产品关系并查询价格表
        List<ProductRelationDTO> relationDTOList = Optional.ofNullable
                (servProductBService.listProductRalationByIdListAndShowType(productIdList, showType))
                .orElse(Collections.emptyList())
                .stream()
                .peek(e -> {
                    // 查询计价器3.0价格表
                    ProductTariffTypeDRO productTariffType = meterFlow3CommonBService.getProductTariffType(query.getChannelId(), e.getProductId(), bizType, ProductDict.TRIFF_TYPE_TARIFF3);
                    if (Objects.nonNull(productTariffType)) {
                        e.setTariffId(productTariffType.getTariffId());
                        e.setTariffType(productTariffType.getTariffType());
                    }
                })
                .filter(e -> Objects.nonNull(e.getTariffType()))
                .collect(Collectors.toList());
        log.info("前台、后台、计价器产品关系:{}", relationDTOList);

        // 产品与价格表id的关系
        Map<Integer, List<ProductRelationDTO>> relationDTOMap = relationDTOList.stream().collect(Collectors.groupingBy(ProductRelationDTO::getCurrentProductId));

        // 处理定金
        this.handleEarnest(productIdList, channelId, cityId, relationDTOList, droList);

        // 处理前后台产品、计价器后台产品一对一的关系
        this.handleOneToOne(relationDTOList, avaliableChannelId);
        log.info("处理一对一关系后,前台、后台、计价器产品关系:{}", relationDTOList);

        // 计算价格、获取项目
        this.handelTariff(relationDTOList, query, droList);

        // 处理没有价格表的数据为30元起
        List<Integer> droProductIdList = droList.stream().map(e -> e.getProductId()).collect(Collectors.toList());
        Set<Integer> set = productIdList.stream().filter(e -> !droProductIdList.contains(e)).collect(Collectors.toSet());
        droList.addAll(this.createNotSupportOnePriceDRO(set, relationDTOMap, query));

        // 支付方式
        droList.forEach(e -> {
            if (NumberUtil.isNullOrZero(e.getPayType())) {
                List<ProductRelationDTO> relationDTOs = relationDTOMap.get(e.getProductId());
                if (!CollectionUtils.isEmpty(relationDTOs)) {
                    SupportPilotVO supportPilotVO = channelPriceBService.listPilotCategs(channelId, cityId,
                            OnePriceConsts.ONE_PRICE, relationDTOs.get(0).getShowServCategId(), relationDTOs.get(0).getShowCategId());
                    e.setPayType(Objects.nonNull(supportPilotVO) ? supportPilotVO.getPayType() : null);
                }
            }
        });
        return droList;
    }

    /**
     * 处理定金
     */
    private void handleEarnest(List<Integer> productIdList, Integer channelId, Integer cityId, List<ProductRelationDTO> relationDTOList, List<ProductPriceDRO3> droList) {
        // 默认产品（没有与后台产品建立关联关系）
        ServProductQuery servProductQuery = new ServProductQuery();
        servProductQuery.setProductIds(productIdList);
        servProductQuery.setDefaultType(GlobalConsts.YES);
        List<ServProduct> defaultProductList = servProductService.listByQuery(servProductQuery);

        // 已处理产品id集合
        List<Integer> handledProductIds = new ArrayList<>();
        // 默认产品的试点
        for (ServProduct productFt : defaultProductList) {
            SupportPilotVO supportPilotVO = channelPriceBService.listPilotCategs(channelId, cityId, OnePriceConsts.ONE_PRICE, productFt.getServCategId(), productFt.getCategId());
            if (Objects.nonNull(supportPilotVO) && NumberUtil.isNotNullOrZero(supportPilotVO.getEarnestPrice()) && !org.apache.commons.collections4.CollectionUtils.isEmpty(supportPilotVO.getServCategIdList())) {
                ProductPriceDRO3 productPriceDRO = new ProductPriceDRO3();
                handledProductIds.add(productFt.getProductId());
                productPriceDRO.setProductId(productFt.getProductId());
                productPriceDRO.setPayType(supportPilotVO.getPayType());
                productPriceDRO.setFixedPrice(GlobalConsts.YES);
                productPriceDRO.setEarnestPrice(supportPilotVO.getEarnestPrice());
                droList.add(productPriceDRO);
            }
        }
        // 有关系产品的试点
        for (ProductRelationDTO dto : relationDTOList) {
            SupportPilotVO supportPilotVO = channelPriceBService.listPilotCategs(channelId, cityId, OnePriceConsts.ONE_PRICE, dto.getShowServCategId(), dto.getShowCategId());
            if (Objects.nonNull(supportPilotVO) && NumberUtil.isNotNullOrZero(supportPilotVO.getEarnestPrice()) && !org.apache.commons.collections4.CollectionUtils.isEmpty(supportPilotVO.getServCategIdList())) {
                if (!handledProductIds.contains(dto.getCurrentProductId())) {
                    ProductPriceDRO3 productPriceDRO = new ProductPriceDRO3();
                    handledProductIds.add(dto.getCurrentProductId());
                    productPriceDRO.setProductId(dto.getCurrentProductId());
                    productPriceDRO.setPayType(supportPilotVO.getPayType());
                    productPriceDRO.setFixedPrice(GlobalConsts.YES);
                    productPriceDRO.setEarnestPrice(supportPilotVO.getEarnestPrice());
                    productPriceDRO.setTariffId(dto.getTariffId());
                    productPriceDRO.setTariffType(dto.getTariffType());
                    droList.add(productPriceDRO);
                }
            }
        }
        // 排除掉定金的产品数据
        List<ProductRelationDTO> newDTOList = relationDTOList.stream().filter(e -> !handledProductIds.contains(e.getCurrentProductId())).collect(Collectors.toList());
        relationDTOList.clear();
        relationDTOList.addAll(newDTOList);
    }

    /**
     * 描述：处理前后台产品、计价器后台产品一对一的关系
     */
    private void handleOneToOne(List<ProductRelationDTO> relationDTOList, Integer channelId) {
        if (CollectionUtils.isEmpty(relationDTOList)) {
            return;
        }
        // 查询渠道后端产品
        ServProductQuery query = new ServProductQuery();
        query.setStatus(GlobalConsts.YES);
        query.setChannelId(channelId);
        query.setShowType(ProductConsts.ERP_PRODUCT_TYPE);
        query.setProductType(ProductDict.CHANNEL_PRODUCT);
        List<Integer> channelProductIds = Optional.ofNullable(servProductService.listByQuery(query))
                .orElse(Collections.emptyList())
                .stream()
                .map(ServProduct::getProductId)
                .collect(Collectors.toList());

        // 前后台产品一对一关系的数据
        List<ProductRelationDTO> oneToOneList = relationDTOList.stream()
                .collect(Collectors.groupingBy(ProductRelationDTO::getShowProductId))
                .values()
                .stream()
                .filter(e -> {
                    // 渠道产品
                    List<ProductRelationDTO> collect1 = e.stream()
                            .filter(m -> ProductDict.CHANNEL_PRODUCT == m.getProductType())
                            .filter(m -> channelProductIds.contains(m.getProductId()))
                            .collect(Collectors.toList());
                    if (collect1.size() > 1) {
                        return false;
                    } else if (collect1.size() == 1) {
                        e.clear();
                        e.add(collect1.get(0));
                        return true;
                    }
                    // 通用产品
                    List<ProductRelationDTO> collect2 = e.stream()
                            .filter(m -> ProductDict.CURRENCY_PRODUCT == m.getProductType())
                            .collect(Collectors.toList());
                    if (collect2.size() > 1) {
                        return false;
                    } else if (collect2.size() == 1) {
                        e.clear();
                        e.add(collect2.get(0));
                        return true;
                    }
                    return false;

                }).flatMap(Collection::stream).collect(Collectors.toList());

        if (Objects.equals(ProductConsts.ET_PRODUCT_TYPE, query.getShowType())) {
            // 计价器产品需额外检查计价器产品和后台产品的一对一关系
            oneToOneList = oneToOneList.stream().collect(Collectors.groupingBy(ProductRelationDTO::getTariffProductId))
                    .values().stream().filter(e -> e.size() == 1).flatMap(Collection::stream).collect(Collectors.toList());
            // 状态
            oneToOneList = oneToOneList.stream().filter(e -> Objects.equals(e.getTariffProductStatus(), GlobalConsts.YES)
                    && Objects.equals(e.getTariffStatus(), GlobalConsts.YES)).collect(Collectors.toList());
        }
        relationDTOList.clear();
        relationDTOList.addAll(oneToOneList);
    }

    /**
     * 描述：计算价格
     */
    private void handelTariff(List<ProductRelationDTO> relationDTOList, ProductPriceQuery query, List<ProductPriceDRO3> droList) {
        if (CollectionUtils.isEmpty(relationDTOList)) {
            return;
        }
        Map<Integer, ProductRelationDTO> relationDTOMap = relationDTOList.stream().collect(Collectors.toMap(e -> e.getProductId(), e -> e, (p, v) -> p));

        // 走系数定价
        List<KvDTO<Integer, Integer>> listRatio = new ArrayList<>(relationDTOList.size());
        for (ProductRelationDTO relationDTO : relationDTOList) {
            KvDTO kvDTO = new KvDTO(relationDTO.getProductId(), relationDTO.getTariffId());
            listRatio.add(kvDTO);
        }
        // 查询item集合
        ProductItemQuery itemQuery = new ProductItemQuery();
        itemQuery.setGeneral(GlobalConsts.YES);
        itemQuery.setKvList(listRatio);
        List<ItemDRO3> itemDROList = tariffProductItem3Service.listItemPriceByQuery(itemQuery);
        log.info("项目itemDROList:{}", itemDROList);
        if (CollectionUtils.isEmpty(itemDROList)) {
            return;
        }

        // 分组Map
        List<Integer> groupIdList = itemDROList.stream().map(m -> m.getGroupId()).distinct().collect(Collectors.toList());
        Map<Integer, ServiceGroup> groupMap = Optional.ofNullable(serviceGroupService.listByGroupIds(groupIdList))
                .orElse(Collections.emptyList())
                .stream()
                .collect(Collectors.toMap(ServiceGroup::getGroupId, r -> r));

        PriceCalDTO calDTO = new PriceCalDTO(query.getChannelId(), query.getCityId(), query.getBrandId(), query.getBizType(), null);

        for (ItemDRO3 itemDRO : itemDROList) {
            // 计算系数后明细
            meterFlow3CommonBService.calPriceDetail(itemDRO, calDTO);
            log.info("项目itemDRO计算价格后数据:{}", itemDRO);

            // 转换出参
            ProductPriceDRO3 productPriceDRO = this.setFixedPriceMsg(itemDRO, relationDTOMap.get(itemDRO.getProductId()), groupMap.get(itemDRO.getGroupId()));
            droList.add(productPriceDRO);
        }
    }

    /**
     * 描述：设置填充属性并返回出参实体
     */
    private ProductPriceDRO3 setFixedPriceMsg(ItemDRO3 itemDRO, ProductRelationDTO relationDTO, ServiceGroup group) {

        itemDRO.setProductName(relationDTO.getProductName());
        itemDRO.setCategOneName(relationDTO.getCategOneName());
        itemDRO.setCategId(relationDTO.getCategId());
        itemDRO.setCategName(relationDTO.getCategName());

        // 查询价格表
        ServiceTariff tariff = serviceTariffService.findByKeyWithCache(itemDRO.getTariffId());
        itemDRO.setTariffName(tariff.getTariffName());
        itemDRO.setTariffType(tariff.getTariffType());

        // 查询后台产品与价格表关系
//        ProductTariffQuery productTariffQuery = new ProductTariffQuery();
//        productTariffQuery.setProductId(itemDRO.getProductId());
//        productTariffQuery.setTariffId(itemDRO.getTariffId());
//        productTariffQuery.setStatus(GlobalConsts.YES);
//        ProductTariffVO productTariff = productTariffService.listByQuery(productTariffQuery)
//                .stream()
//                .findFirst()
//                .orElse(new ProductTariffVO());
//        itemDRO.setDesc(productTariff.getDesc());
//        itemDRO.setSpecialDesc(productTariff.getSpecialDesc());

        if (Objects.nonNull(group)) {
            itemDRO.setGroupName(group.getGroupName());
            itemDRO.setGroupSort(group.getSort());
        }

        ProductPriceDRO3 productPriceDRO = new ProductPriceDRO3();
        productPriceDRO.setProductId(relationDTO.getCurrentProductId());
        productPriceDRO.setPrice(itemDRO.getPrice());
        productPriceDRO.setExternalSettlementPrice(itemDRO.getExternalSettlementPrice());
        productPriceDRO.setInternalSettlementPrice(itemDRO.getInternalSettlementPrice());
        productPriceDRO.setFixedPrice(GlobalConsts.YES);
        productPriceDRO.setItemId(itemDRO.getItemId());
        productPriceDRO.setTariffId(itemDRO.getTariffId());
        productPriceDRO.setTariffType(itemDRO.getTariffType());
        productPriceDRO.setItemDRO3(itemDRO);
        return productPriceDRO;
    }

    /**
     * 返回非一口价（30元起、最低服务项价格）
     */
    private List<ProductPriceDRO3> createNotSupportOnePriceDRO(Set<Integer> productIds, Map<Integer, List<ProductRelationDTO>> relationDTOMap, ProductPriceQuery query) {

        if (CollectionUtils.isEmpty(productIds)) {
            return Collections.emptyList();
        }
        return productIds.stream().map(e -> {
            ProductPriceDRO3 dro = new ProductPriceDRO3();
            dro.setProductId(e);
            dro.setFixedPrice(GlobalConsts.NO);
            if (CollectionUtils.isEmpty(relationDTOMap)) {
                return dro;
            }
            List<ProductRelationDTO> relationDTOs = relationDTOMap.get(e);
            if (CollectionUtils.isEmpty(relationDTOs)) {
                return dro;
            }
            dro.setTariffId(relationDTOs.get(0).getTariffId());
            dro.setTariffType(relationDTOs.get(0).getTariffType());

            // 查询产品的最低服务项价格
            List<KvDTO<Integer, Integer>> collect = new ArrayList<>();
            for (ProductRelationDTO relationDTO : relationDTOs) {
                KvDTO<Integer, Integer> kv = new KvDTO<>(relationDTO.getProductId(), relationDTO.getTariffId());
                collect.add(kv);
            }
            // 查询item集合
            ProductItemQuery itemQuery = new ProductItemQuery();
            itemQuery.setKvList(collect);
            itemQuery.setItemCategId(ItemCategoryConsts.MAINTAIN_ITEM);
            ItemDRO3 minItemDRO = Optional.ofNullable(tariffProductItem3Service.listItemPriceByQuery(itemQuery))
                    .orElse(Collections.emptyList())
                    .stream()
                    .peek(item -> item.setPrice(item.getPartPrice() + item.getTrafficFee() + item.getHourFee()))
                    .sorted(Comparator.comparing(ItemDRO3::getPrice)).findFirst().get();
            if (Objects.nonNull(minItemDRO)) {
                PriceCalDTO calDTO = new PriceCalDTO(query.getChannelId(), query.getCityId(), query.getBrandId(), query.getBizType(), null);
                meterFlow3CommonBService.calPriceDetail(minItemDRO, calDTO);
                dro.setPrice(minItemDRO.getPrice());
            }
            return dro;
        }).collect(Collectors.toList());
    }

}
